package domain

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"time"

	"cloudea.com/container/config"
	"cloudea.com/container/domain/container"
	"cloudea.com/container/domain/image"
	"cloudea.com/container/utils"
)

/** Application Service */

type ApplicationService struct{}

/** 接取并保存镜像 */
func (app *ApplicationService) Pull(name string, tag string) {
	im := image.NewImage(name, tag)
	err := image.ImageSave(im)
	if err == nil {
		fmt.Println("pull successfully")
	} else {
		fmt.Println("pull error: " + err.Error())
	}
}

/** 列出所有的镜像 */
func (app *ApplicationService) Images() {
	images := image.ImageList()
	fmt.Printf("%10s\t%15s\t%20s\t%20v\n", "IMAGE HASH", "IMAGE NAME", "IMAGE TAG", "CREATE TIME")
	for _, image := range images {
		fmt.Printf(
			"%10s\t%15s\t%20s\t%20v\n",
			image.GetSimpleHash(),
			image.GetName(),
			image.GetTag(),
			image.GetCreateTime().Format("2006/02/01 15:04:05"),
		)
	}
}

/** 删除一个镜像 */
func (app *ApplicationService) Rmi(name string, tag string) {
	im, _ := image.ImageGet(name, tag)
	if im == nil {
		fmt.Printf("名为%s:%s的镜像不存在\n", name, tag)
		return
	}
	err := image.ImageRemove(im)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("成功删除 %s\n", im.GetFullName())
}

/** 运行一个容器 */
func (app *ApplicationService) Run(
	name string,
	tag string,
	containerName string,
	isDaemon bool,
	cpuQuota string,
	memQuota string,
	cmd []string) {
	img, err := image.ImageGet(name, tag)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	// 当容器不存在时创建， 否则运行已有的
	cont, err := container.REPOSITORY.GetContainer(containerName)
	if cont == nil || err != nil {
		ip, err := app.genAvailableIP()
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		cont = container.NewContainer(
			containerName,
			*img,
			container.CONTAINER_STOPPED,
			cmd,
			time.Now(),
			0,
			ip,
		)
	}

	if cont == nil {
		fmt.Println("创建失败")
		return
	}
	err = cont.Run(isDaemon, func() {
		// 当子进程一旦运行
		// 更新配置（主要是更新进程id）
		utils.Assert(container.REPOSITORY.Save(*cont))
		// 限制资源
		cont.LimitResource(cpuQuota, memQuota)
	})
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

/** 运行一个容器(在新的名字空间中) */
func (app *ApplicationService) Run2(containerName string) {
	cont, err := container.REPOSITORY.GetContainer(containerName)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	err = cont.Run2()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

/** 显示当前已经创建的容器 */
func (app *ApplicationService) Ps(showAll bool) {
	// 从repo获取所有的容器，按照时间例序排序
	containers := container.REPOSITORY.GetContainers()
	sort.Slice(containers, func(i, j int) bool {
		a := containers[i].CreatedTime
		b := containers[j].CreatedTime
		return b.Before(a)
	})
	// 依次打印信息
	fmt.Printf("%10s\t%14s\t%20s\t%20v\t%20s\t%s\n", "HASH CODE ", "CONTAINER NAME", "IMAGE:TAG", "CREATE TIME", "STATUS", "COMMANDS")
	for _, con := range containers {
		// 如果没有 -a标志， 则隐藏没有在运行的容器
		if !showAll && !(con.Status == container.CONTAINER_RUNNING) {
			continue
		}
		commandString := strings.Join(con.GetFullCommand(), " ")
		fmt.Printf(
			"%10s\t%14s\t%20s\t%20v\t%20s\t%s\n",
			con.Hash,
			con.Name,
			con.Image.GetFullName(),
			con.CreatedTime.Format("2006/02/01 15:04:05"),
			con.Status,
			commandString,
		)
	}
}

/** 删除容器 */
func (app *ApplicationService) Rm(containerName string) {
	con, err := container.REPOSITORY.GetContainer(containerName)
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
	err = container.REPOSITORY.Delete(*con)
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
	fmt.Printf("成功删除 %s\n", containerName)
}

/** 在容器中执行命令 */
func (app *ApplicationService) Exec(containerName string, isDeamon bool, cmd []string) {
	con, err := container.REPOSITORY.GetContainer(containerName)
	if err != nil {
		fmt.Printf("容器不存在：%v\n", err)
		return
	}
	err = con.Exec(isDeamon, cmd)
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
}

/** 为容器生成一个可用的IP地址 */
func (app *ApplicationService) genAvailableIP() (string, error) {
	net, mask := utils.IPCIDRParse(config.CONTAINER0_CIDR)
	//bridgeIP = net + 1

	// 获取已有的IP
	var ipSet map[int]bool = make(map[int]bool)
	containers := container.REPOSITORY.GetContainers()
	for _, con := range containers {
		containerIP, _ := utils.IPAdrrToInt(con.Ip)
		ipSet[containerIP] = true
	}

	// 迭代生成IP (保留0, 1两个地址， 0 不用，1给网桥使用)
	ipCapaciy := (^mask) & 0xFFFFFFFF
	for i := 2; i < ipCapaciy; i++ {
		ipAddr := net + i
		var _, isOk = ipSet[ipAddr]
		if !isOk {
			return utils.IntToIPAddr(ipAddr), nil
		}
	}

	return "", errors.New("没有足够的容量分配IP地址")
}

var APPLICATION = ApplicationService{}
